// Package user
// Created by GoLand
// @User: lenora
// @Date: 2023/11/27
// @Time: 14:46

package user

import (
	"camp-homework/week02/internal/domain"
	"camp-homework/week02/internal/middlewares"
	"camp-homework/week02/internal/service"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"net/http"
	"regexp"
	"unicode/utf8"
)

const (
	passwordRegexPattern = `^[a-zA-Z]\w{5,17}$`
	birthRegexPattern    = `^((?:19[2-9]\d{1})|(?:20(?:(?:0[0-9])|(?:1[0-9])|(?:2[0-3]))))\-((?:0?[1-9])|(?:1[0-2]))\-((?:0?[1-9])|(?:[1-2][0-9])|30|31)$`
)

type UserHandler struct {
	passwordRexExp *regexp.Regexp
	birthRexExp    *regexp.Regexp
	srv            *service.UserService
}

func NewUserHandler(srv *service.UserService) *UserHandler {
	return &UserHandler{
		passwordRexExp: regexp.MustCompile(passwordRegexPattern),
		birthRexExp:    regexp.MustCompile(birthRegexPattern),
		srv:            srv,
	}
}

func (h *UserHandler) RegisterRoutes(engine *gin.Engine) {
	uNone := engine.Group("/users/")
	uNone.POST("signup", h.SignUP)
	uNone.POST("login", h.Login)
	uNone.GET("profile", h.Profile)
	uNone.POST("edit", h.Edit)
}

func (h *UserHandler) Edit(ctx *gin.Context) {
	type editReq struct {
		Nickname string `json:"nickname"`
		Birth    string `json:"birth"`
		Desc     string `json:"desc"`
	}

	var req editReq
	if err := ctx.Bind(&req); err != nil {
		ctx.JSON(http.StatusOK, "param error")
		return
	}
	uid := h.userId(ctx)

	if req.Nickname != "" {
		if utf8.RuneCountInString(req.Nickname) >= 50 {
			ctx.JSON(http.StatusOK, "desc is too long")
			return
		}
	}

	if req.Birth != "" {
		if isBirth := h.birthRexExp.Match([]byte(req.Birth)); !isBirth {
			ctx.JSON(http.StatusOK, "wrong birth")
			return
		}
	}
	if req.Desc != "" {
		if utf8.RuneCountInString(req.Desc) > 200 {
			ctx.JSON(http.StatusOK, "desc is too long")
			return
		}
	}

	err := h.srv.Edit(ctx, uid, req.Nickname, req.Birth, req.Desc)
	switch err {
	case nil:
		ctx.JSON(http.StatusOK, "success")
	case service.ErrUserNotFound:
		ctx.JSON(http.StatusOK, "用户不存在")
	default:
		ctx.JSON(http.StatusOK, "internal error:"+err.Error())
	}
	return
}

func (h *UserHandler) Login(ctx *gin.Context) {
	type loginReq struct {
		UserName string `json:"userName" binding:"required"`
		Password string `json:"password" binding:"required"`
	}

	var req loginReq
	if err := ctx.Bind(&req); err != nil {
		ctx.JSON(http.StatusOK, "param error")
		return
	}

	u, err := h.srv.Login(ctx, req.UserName, req.Password)
	switch err {
	case nil:
		//设置session
		sess := sessions.Default(ctx)
		sess.Set(middlewares.SessionKey, u.Id)
		sess.Options(sessions.Options{
			//session有效期(秒)
			MaxAge: 60 * 10,
		})
		if err := sess.Save(); err != nil {
			ctx.JSON(http.StatusOK, "login error:"+err.Error())
			return
		}
		ctx.JSON(http.StatusOK, "login success")
	case service.ErrInvalidUser:
		ctx.JSON(http.StatusOK, "账号或密码错误")
	default:
		ctx.JSON(http.StatusOK, "login error:"+err.Error())
	}
	return

}

func (h *UserHandler) Profile(ctx *gin.Context) {
	uid := h.userId(ctx)
	u, err := h.srv.Profile(ctx, uid)
	switch err {
	case nil:
		ctx.JSON(http.StatusOK, u)
	case service.ErrUserNotFound:
		ctx.JSON(http.StatusOK, "用户不存在")
	default:
		ctx.JSON(http.StatusOK, "internal error:"+err.Error())
	}
	return
}

func (h *UserHandler) SignUP(ctx *gin.Context) {
	type signUpReq struct {
		UserName        string `json:"userName" binding:"required"`
		Password        string `json:"password" binding:"required"`
		ConfirmPassword string `json:"confirmPassword" binding:"required"`
	}
	var req signUpReq
	if err := ctx.Bind(&req); err != nil {
		ctx.JSON(http.StatusOK, "param error")
		return
	}

	if req.Password != req.ConfirmPassword {
		ctx.JSON(http.StatusOK, "the passwords are not same word")
		return
	}

	isPwd := h.passwordRexExp.Match([]byte(req.Password))
	if !isPwd {
		ctx.JSON(http.StatusOK, "param wrong")
		return
	}

	err := h.srv.Signup(ctx, domain.UserBase{
		UserName: req.UserName,
		Password: req.Password,
	})

	switch err {
	case nil:
		ctx.JSON(http.StatusOK, "you are signing up")
	case service.ErrDuplicateUserName:
		ctx.JSON(http.StatusOK, "用户冲突")
	default:
		ctx.JSON(http.StatusOK, err.Error())
	}
	return
}

func (h *UserHandler) userId(ctx *gin.Context) uint64 {
	sess := sessions.Default(ctx)
	uid, ok := sess.Get(middlewares.SessionKey).(uint64)
	if !ok {
		ctx.AbortWithStatus(http.StatusUnauthorized)
		return 0
	}
	return uid
}
